Aller au contenu principal

List et Map

Objectif : typer vos collections pour la sûreté et l'auto-complétion.

Pourquoi des types génériques ?

  • List<T> garantit que chaque élément est de type T.
  • Map<K,V> garantit le type des clés (K) et des valeurs (V).
  • Moins d'erreurs runtime, meilleure complétion IDE et tri/filtrage sûrs.

List

final List<String> fruits = ['pomme', 'poire'];
fruits.add('mangue');

// Littéral typé sans répétition
final fruits2 = <String>['pomme', 'poire'];

// Parcours typé
for (final fruit in fruits2) {
print(fruit.toUpperCase());
}

Points clés :

  • Préférez déclarer le type à gauche ou via le littéral <T> [...].
  • List.unmodifiable([...]) pour une vue immuable ; const [] pour compile-time constants.
  • list.where(...), map(...), sort(...) restent typés et sûrs.

Exemples supplémentaires (List) :

final notes = <int>[12, 8, 15];

// Ajout d'un élément
notes.add(10);

// Accès par index
final premiere = notes[0]; // 12

// Remplacement
notes[1] = 9;
final noms = <String>['Ana', 'Ben', 'Chloé'];

// Filtrer
final avecA = noms.where((n) => n.startsWith('A')).toList();

// Transformer
final majuscules = noms.map((n) => n.toUpperCase()).toList();
final scores = <int>[5, 1, 3];

// Trier (modifie la liste)
scores.sort();

// Vérifier l'existence d'un élément
final contient3 = scores.contains(3); // true

Exemple de transformations typées (where, map, fold) :

final notes = <int>[12, 8, 15, 9];

// Garder les notes >= 10, les convertir en /20, puis sommer
final totalSur20 = notes
.where((n) => n >= 10)
.map((n) => n * 2)
.fold<int>(0, (acc, n) => acc + n);

// totalSur20 = 54 (12*2 + 15*2)

Map

final Map<String, int> scores = {
'alice': 10,
'bob': 7,
};

scores['carol'] = 12;

// Littéral typé
final scores2 = <String, int>{
'dan': 5,
};

// Lecture sécurisée avec valeur par défaut
final pointsAlice = scores['alice'] ?? 0;

Points clés :

  • Typage des clés et valeurs : <String, int>{ ... }.
  • putIfAbsent(key, () => value) pour insérer si absent.
  • Itérations : map.entries, map.keys, map.values.

Exemples map.entries, map.keys, map.values :

final scores = <String, int>{
'alice': 10,
'bob': 7,
};

// entries : paires clé/valeur
for (final entry in scores.entries) {
print('${entry.key} -> ${entry.value}');
}

// keys : toutes les clés
for (final name in scores.keys) {
print(name.toUpperCase());
}

// values : toutes les valeurs
final total = scores.values.reduce((a, b) => a + b);
print(total); // 17

Exemples putIfAbsent :

final stock = <String, int>{
'pomme': 3,
};

// Insère si absent
stock.putIfAbsent('poire', () => 5);
// N'écrase pas si présent
stock.putIfAbsent('pomme', () => 99);
// stock => {'pomme': 3, 'poire': 5}
final visites = <String, int>{};

void visite(String page) {
// Initialise à 0 si absent, puis incrémente
final current = visites.putIfAbsent(page, () => 0);
visites[page] = current + 1;
}

visite('/accueil');
visite('/accueil');
// visites => {'/accueil': 2}
final cache = <String, String>{};

String chargerProfil(String id) {
// La fonction n'est appelée que si la clé est absente
return cache.putIfAbsent(id, () {
return 'profil_$id';
});
}

final a = chargerProfil('u1');
final b = chargerProfil('u1');
// a et b viennent du cache, la fonction n'est exécutée qu'une fois

Spreads et collection-if/for (rappel)

final base = <int>[1, 2];
final extended = <int>[0, ...base, 3];
final maybeNull = <int>[];
final safe = <int>[...?, maybeNull]; // n'ajoute rien si null

final filtered = [
for (final x in extended)
if (x.isEven) x,
];

Casts sur collections

final raw = <dynamic>['a', 'b'];
final strings = raw.cast<String>(); // vérifie les éléments à l'usage

Bonnes pratiques

  • Évitez List ou Map sans type : utilisez List<T> / Map<K,V>.
  • Préférez les transformations typées (map, where, fold) plutôt que des boucles qui mélangent les types.
  • Utilisez cast<T>() avec parcimonie ; idéalement, construisez vos collections déjà typées (ex. via fromJson).